package com.znv.sentinelrepository;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;
import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author pengYongQiang
 * @version 1.0
 * @date 2020/7/7 14:20
 */
public class InMySqlMetricsRepository implements MetricsRepository<MetricEntity> {
    private final static Logger LOG = LoggerFactory.getLogger(InMySqlMetricsRepository.class);

    @Autowired
    private MetricEntityDao dao;

    @Override
    public void save(MetricEntity metric) {
        String s = JSON.toJSONString(metric);
        MetricEntityHolder metricEntityHolder = JSONObject.parseObject(s, MetricEntityHolder.class);
        dao.save(metricEntityHolder);
    }

    @Override
    public void saveAll(Iterable<MetricEntity> metrics) {
        if (metrics == null) {
            return;
        }
        metrics.forEach(this::save);
    }

    @Override
    public List<MetricEntity> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) {
        List<MetricEntity> results = new ArrayList<>();

        Date start = new Date(startTime);
        Date end = new Date(endTime);

        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");

        LOG.info("查询[{}]服务 -> 资源[{}] -> 时间段[{}~{}]",app,resource,format.format(start),format.format(end));

        List<MetricEntityHolder> list = dao.findAllByAppAndResourceAndTimestampBetween(app, resource, start, end);
        if (CollectionUtils.isEmpty(list) ) {
            return results;
        }
        String jsonString = JSON.toJSONString(list);
        return  JSONArray.parseArray(jsonString, MetricEntity.class);
    }

    @Override
    public List<String> listResourcesOfApp(String app) {
        /*
         * step1.数据库获取资源
         */
        List<MetricEntityHolder> allByApp = dao.findAllByApp(app);

        final long minTimeMs = System.currentTimeMillis() - 1000 * 60;
        //key:资源名，value:MetricEntity
        Map<String, MetricEntityHolder> resourceCount = new ConcurrentHashMap<>(32);

        /*
         * step2.累加pqs,目的是： 通过资源的qps进行资源排序。
         *
         * 如果一个app下有多个资源，
         * 对每一个资源进行累加，从而计算出资源的总qps，然后进行排序。
         */
        for (MetricEntityHolder holder : allByApp) {
            /*long timestamp = holder.getTimestamp().getTime();
            if (timestamp < minTimeMs){
                continue;
            }*/
            String resource = holder.getResource();
            if (resourceCount.containsKey(resource)) {
                //针对某个资源，循环累加Qqs，从而计算出某个时间段的Qqs
                MetricEntityHolder old = resourceCount.get(resource);
                old.addPassQps(holder.getPassQps());
                old.addRtAndSuccessQps(holder.getRt(), holder.getSuccessQps());
                old.addBlockQps(holder.getBlockQps());
                old.addExceptionQps(holder.getExceptionQps());
                old.addCount(1);
            } else {
                resourceCount.put(resource, holder);
            }
        }

        /*
         * step3.通过pqs对资源名称排序
         */
        List<String> collect = resourceCount.entrySet().stream()
                .sorted((o1, o2) -> {
                    MetricEntityHolder e1 = o1.getValue();
                    MetricEntityHolder e2 = o2.getValue();
                    int t = e2.getBlockQps().compareTo(e1.getBlockQps());
                    if (t != 0) {
                        return t;
                    }
                    return e2.getPassQps().compareTo(e1.getPassQps());
                })
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        return collect;
    }


}
